package mSocket.NettyImooc;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.websocketx.*;
import io.netty.util.CharsetUtil;

import java.util.Date;

/**
 * @Author: Jie
 * @Date: 2018/12/22 15:42
 * @Function: 接受、处理、响应客户端
 */
public class MyWebSocketHandler extends SimpleChannelInboundHandler<Object> {
private WebSocketServerHandshaker handshaker;
private static final String WEB_SOCKET_URL="ws://localhost:8888/websocket";

    //客户端与服务端创建连接 时调用
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
//        super.channelActive(ctx);
        NettyConfig.group.add(ctx.channel());
        System.out.println("client and server channelActive start... ");
    }

    //断开连接时调用
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
//        super.channelInactive(ctx);
        NettyConfig.group.remove(ctx.channel());
        System.out.println("client and server channelInactive close...");
    }

    //服务端接受客户端发送过来的数据结束之后调用
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
//        super.channelReadComplete(ctx);
        ctx.flush();
        System.out.println("client and server channelReadComplete ...");
    }

    //工程异常的时候调用
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
//        super.exceptionCaught(ctx, cause);
        System.err.println("exceptionCaught.......");
        cause.printStackTrace();
        ctx.close();
    }

    //服务端 处理 客户端websocket请求的和核心方法
    @Override
    protected void channelRead0(ChannelHandlerContext channelHandlerContext, Object msg) throws Exception {
        System.out.println("channelRead0 received client msg.....");
       //处理客户端 发起的http 握手请求服务
        if (msg instanceof FullHttpRequest){
            handHttpRequest(channelHandlerContext,(FullHttpRequest)msg);
        }else if (msg instanceof WebSocketFrame) {//处理websocket 连接业务
            handWebsocketFrame(channelHandlerContext, (WebSocketFrame)msg);
        }
    }

    /**
     * 处理客户端向服务端发起的http握手请求的业务
     * @param ctx
     * @param req
     */
    private void handHttpRequest(ChannelHandlerContext ctx,FullHttpRequest req){
        // TODO: 2018/12/22 寻找替代方法
        //客户端向服务端 非websocket发起http请求
        if (!req.getDecoderResult().isSuccess()
                ||!("websocket".equals(req.headers().get("Upgrade")))){//特殊的请求头 websocket
            sendHttpResponse(ctx,req,new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,HttpResponseStatus.BAD_REQUEST));//返回 400
            return;
        }
        //如果是 websocket http握手请求
        WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(WEB_SOCKET_URL,
                null,false);
        handshaker = wsFactory.newHandshaker(req);
        if (handshaker == null){
            WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());
        }else {
            handshaker.handshake(ctx.channel(),req);
        }
    }

    /**
     * 服务端向客户端响应消息
     * @param ctx
     * @param req
     * @param res
     */
    private void sendHttpResponse(ChannelHandlerContext ctx, FullHttpRequest req, DefaultFullHttpResponse res){
        if (res.getStatus().code()!=200){
            ByteBuf buf = Unpooled.copiedBuffer(res.getStatus().toString(), CharsetUtil.UTF_8);
            res.content().writeBytes(buf);
            buf.release();
        }
        //服务端向客户端发送数据
        ChannelFuture channelFuture = ctx.channel().writeAndFlush(res);
        if (res.getStatus().code()!=200){
            channelFuture.addListener(ChannelFutureListener.CLOSE);//关闭链接。  为什么是addlistener？？？
        }
    }

    /**
     * 处理客户端与服务端之间的 websocket业务
     * @param ctx
     * @param frame
     */
    private void handWebsocketFrame(ChannelHandlerContext ctx,WebSocketFrame frame){
        //判断是否是关闭指令
        if (frame instanceof CloseWebSocketFrame){
            handshaker.close(ctx.channel(),(CloseWebSocketFrame)frame.retain());
        }
        //判断是否Ping消息
        if (frame instanceof PingWebSocketFrame){
            ctx.channel().write(new PongWebSocketFrame(frame.content().retain()));
            return;
        }
        //判断是否是 【二进制】消息，TODO 现在不支持二进制消息，抛出异常 只有2中  1、文本 2、二进制
        if (!(frame instanceof TextWebSocketFrame)){
            System.out.println("currently we are not support 0 1 message...");
            throw new RuntimeException("【"+this .getClass().getName()+"】not support message ...");
        }
        //返回应答消息
        String request = ((TextWebSocketFrame)frame).text();//获取client发送的请求消息
        System.out.println("server received client msg ===>>>>>"+request);
        TextWebSocketFrame textWebSocketFrame = new TextWebSocketFrame(new Date().toString() + " I am server ...channel id== " + ctx.channel().id() + "--" + request);

        //单独发。。。
//        ctx.write(textWebSocketFrame);

        //群发，向每个服务端发送。
        NettyConfig.group.write(textWebSocketFrame);

    }

}
